/**
 * this is the static authentication class that still needs work. See ref for sample
 * implementation
 *
 * reference: http://www.public.iastate.edu/~java/docs/guide/security/Acl.html
 */

package pos.auth;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.acl.Permission;
import java.security.acl.Group;
import java.security.acl.Acl;
import java.security.acl.AclEntry;
import java.security.acl.NotOwnerException;
import sun.security.acl.PrincipalImpl;
import sun.security.acl.PermissionImpl;
import sun.security.acl.GroupImpl;
import sun.security.acl.AclImpl;
import sun.security.acl.AclEntryImpl;
import java.security.Principal;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import pos.interfaces.Database;
import pos.storage.StorageFactory;
import pos.storage.db.sql.SQL;

/**
 *
 * @author Parvez
 */
public class Authentication {
//    public static Connection systemConn = null;
//    public static Connection clerkConn = null;
    public static Connection adminConn = null;

    private static Principal clerk = new PrincipalImpl("clerk"); /* sample users */
    private static Principal customer = new PrincipalImpl("customer");
    private static Principal admin = new PrincipalImpl("admin");

    private static Permission none = new PermissionImpl("NONE"); /* sample permissions */
    private static Permission read = new PermissionImpl("READ");
    private static Permission write = new PermissionImpl("WRITE");
    private static Permission modify = new PermissionImpl("MODIFY");
    private static Permission all = new PermissionImpl("ALL");
    private static Permission adminCons = new PermissionImpl("ADMIN_CONSOLE");
    private static Permission helpCust = new PermissionImpl("HELP_CUSTOMER");

    private static Group g1 = new GroupImpl("admin");  /*sample groups */
    private static Group g2 = new GroupImpl("clerk");
    private static Group g3 = new GroupImpl("customer");

    //
    // create a new acl with the name "exampleAcl"
    //
//    private static Acl acl1 = new AclImpl(admin, "exampleAcl1");
//    private static Acl acl2 = new AclImpl(clerk, "exampleAcl2");
//    private static Acl acl3 = new AclImpl(customer, "exampleAcl3");

    //
    // Allow group all permissions
    //
    private static AclEntry entry1 = new AclEntryImpl(g1);
    private static AclEntry entry2 = new AclEntryImpl(g2);
    private static AclEntry entry3 = new AclEntryImpl(g3);

    //
    // Take away WRITE permissions for
    // user1. All others in groups still have
    // WRITE privileges.
    //
    
    
    //save user auth attribs
    private static Principal userPrn = null;
    private static Group userGrp = null;
    private static Acl userAcl = null;
    private static AclEntry userEntry = null;
    
    //save employee table information from database
    private static String userName = null;
    private static int eid = 0;
    private static String empDesignation = null;
    private static int authLevel = 0;

    /**
     * default constructor
     *
     * this method should create a user with necessary permissions using the
     * java ACL data structures
     */
   public Authentication() {
        //
        // This enumeration should have "READ" and"WRITE"
        // permissions. 
        //

        // This enumeration is an enumeration of
        // Permission interfaces. It should return
        // only "READ" permission.
//        Enumeration e1 = acl1.getPermissions(admin);
//        Enumeration e2 = acl2.getPermissions(clerk);
        userPrn = null;
        userGrp = null;
        userAcl = null;
        userEntry = null;
//        Enumeration e3 = acl2.getPermissions(customer);
   }

   /**
    * this method checks to see if a user has permission to something or not
    *
    * @param acl Acl user
    * @param p Permission name of permission
    * @return boolean true if the acl user has permission p; false otherwise
    */
   public static boolean hasPermission(Permission per) {
       if(userGrp != null && userGrp.toString().equalsIgnoreCase("admin")) {
           return true;
       }
       if(userEntry != null && userEntry.checkPermission(per)) {
           return true;
       }

        return false;//same usage
   }

   /**
    * add a member to group. note that member of a group inherits all priveleges
    * 
    * @param member Principle the member that needs to be added to the group
    * @param group Group the user group
    */
    protected static void addMemberToGroup(Principal member, Group group) {
        group.addMember(member);
    }

    /**
     * add permission to an entry. catch exception on failure
     *
     * @param p Permission to be granted to acl entry
     * @param entry AclEntry the entry group that needs to be granted to
     * @param acl Acl the acl name
     */
    protected static void addPermissions(Permission p, AclEntry entry, Principal pr, Acl acl) {
        entry.addPermission(p);
        try {
            acl.addEntry(pr, entry);
        } catch(NotOwnerException e) {
            System.out.println("No owner exception caught: " + e);
        }
    }

//    /**
//     * not fully thought out yet...
//     */
//    protected static void addEntry() {
//        entry2.addPermission(write);
//        entry2.setNegativePermissions();
//        try {
//            acl1.addEntry(admin, entry2);
//        } catch(NotOwnerException e) {
//            System.out.println("No owner exception caught: " + e);
//        }
//    }

    /**
     *
     * @param userName String name of the user
     * @param cardNumber Integer generate a random password
     * @return
     */
    protected static String genPassword(String userName, Integer cardNumber) {
        String str = userName + cardNumber;
        String hash = getMD5Digest(str);

        return hash;
    }

    /**
     *
     * @param cardNumber Integer the card number
     * @return String User password from database
     */
    protected static String getPassword(String userName){
        String pswd = null;
        try{
            //Getting a DB Connection
            ResultSet rs;
            Database mysql = (Database) StorageFactory.getStorage(StorageFactory.DBTYPE, Database.MYSQL);
            adminConn = mysql.getConnection(Database.ADMIN);
            Statement stmt = adminConn.createStatement();
            String query1 = SQL.AuthGetPassword;
            rs = stmt.executeQuery(query1);
            while(rs.next()) {
                if(rs.getString("EmployeeUserName").compareTo(userName) == 0) {
                    pswd = rs.getString("EmployeePassword");
                    eid = rs.getInt("EmployeeId");
                    userName = rs.getString("EmployeeUserName");
                    authLevel = rs.getInt("EmployeeAuthorizationLevel");
                    empDesignation = rs.getString("EmployeeDesignation");
//                    System.out.println(authLevel);
                    //set auth attribs
                    setAuthAttribs();
                    break;
                } else {
                    pswd = "";
                }
            }
            adminConn.close();
            return pswd;
        }
        catch(SQLException e){
            System.out.println("query: "+e);
            e.printStackTrace();
        }
        catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        return pswd;
    }

    /**
     * 
     * @param password
     * @param eid
     * @return
     */
    protected static boolean setPassword(String password, int eid) {
        try{
            //Getting a DB Connection
            ResultSet rs;
            Database mysql = (Database) StorageFactory.getStorage(StorageFactory.DBTYPE, Database.MYSQL);
            adminConn = mysql.getConnection(Database.ADMIN);
            Statement stmt = adminConn.createStatement();
            String query = "UPDATE pos.employee SET employee.EmployeePassword = '" + password + "' WHERE employee.EmployeeId = " + eid;
            rs = stmt.executeQuery(query);
            adminConn.close();
        }
        catch(SQLException e){
            e.printStackTrace();
            return false;
        }
        catch(ClassNotFoundException e){
            e.printStackTrace();
            return false;
        }

        return true;
    }
    
    /**
     * Verifies whether the user is logged in or not
     * 
     * @return boolean true if user is logged in; false otherwise
     */
    public static boolean isLoggedIn() {
        if(userPrn != null && userGrp != null && userAcl != null && userEntry != null)
            return true;
        return false;
    }
    
    /**
     * this method sets the user auth attributes
     */
    private static void setAuthAttribs() {
        switch(authLevel) {
            case 1:
                /* all admin related permissions would be allowed */
                userPrn = new PrincipalImpl("admin");
                userGrp = new GroupImpl("admin");
                userAcl = new AclImpl(admin, "adminAcl");
                userEntry = new AclEntryImpl(userGrp);
                userEntry.addPermission(all);
                break;
            case 2:
                /* all clerk related permissions would be allowed */
                userPrn = new PrincipalImpl("clerk");
                userGrp = new GroupImpl("clerk");
                userAcl = new AclImpl(clerk, "clerkAcl");
                userEntry = new AclEntryImpl(userGrp);
                userEntry.addPermission(read);
                userEntry.addPermission(modify);
                userEntry.addPermission(helpCust);
                break;
            case 3:
                /* all customer related permissions would be allowed */
                userPrn = new PrincipalImpl("customer");
                userGrp = new GroupImpl("customer");
                userAcl = new AclImpl(clerk, "customerAcl");
                userEntry = new AclEntryImpl(userGrp);
                userEntry.addPermission(read);
                userEntry.addPermission(write);
                break;
            default:
                userPrn = null;
                userGrp = null;
                userAcl = null;
                userEntry = null;
        }
    }
    
    /**
     * unset all auth attributes -> preparation for logout
     */
    private static void unsetAuthAttribs() {
        userPrn = null;
        userGrp = null;
        userAcl = null;
        userEntry = null;
    }

    /**
     * this public method varifies if the user exists in the employee database or not
     * 
     * @return boolean True if user is valid; false otherwise
     */
    public static boolean isValidUser(String userName, String pswd){
        if(!getPassword(userName).isEmpty() && getPassword(userName).equalsIgnoreCase(pswd)) {
            return true;
        }
        return false;
    }

    /**
     * reference: http://javacream.blogspot.com/2008/07/java-md5-hash-sample.html
     * this method converts strings to md5 hashed strings using java's default
     * libraries
     *
     * @param str String that needs to be converted to an MD5 hash
     * @return hashed string
     */
    private static String getMD5Digest(String str) {
        try {
            byte[] buffer = str.getBytes();
            byte[] result = null;
            StringBuffer buf = null;
            MessageDigest md5 = MessageDigest.getInstance("MD5");

            //allocate room for the hash result = new byte[md5.getDigestLength()];
            //calculate hash md5.reset();
            md5.update(buffer);
            result = md5.digest();
            System.out.println(result);

            //create hex string from the 16-byte hash buf = new StringBuffer(result.length * 2);
            for (int i = 0; i < result.length; i++) {
                int intVal = result[i]& 0xff;
                if (intVal < 0x10) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(intVal).toUpperCase());
            }

            return buf.toString();

        } catch (NoSuchAlgorithmException e) {
            System.err.println("Exception: " + e);
            e.printStackTrace();
        }

        return null;
    }
    
    /**
     * Unset all logged in attributes
     * 
     * @return boolean true if the user is logged in and the auth attributes 
     * were unset; false otherwise
     */
    public static boolean logOut() {
        if(isLoggedIn()) {
            unsetAuthAttribs();
            return true;
        }
        return false;
    }
}
